<!doctype html>

<title>16 Advanced Integration - React From Zero</title>

<script src="https://unpkg.com/react@15.4.2/dist/react.js"></script>
<script src="https://unpkg.com/react-dom@15.4.2/dist/react-dom.js"></script>
<script src="https://unpkg.com/babel-core@5.8.38/browser.min.js"></script>

<script src="https://unpkg.com/d3@4.7.3/build/d3.min.js">
// Sometimes we need to integrate more complex
// libraries. Libraries that want to use the
// DOM directly or require asynchronous interaction.
// In this example we use D3.js, a free InfoVis library.
</script>

<div id="app"></div>

<script type="text/babel">

// Since D3 needs to interact directly with the DOM we
// should use a component class, because it can store
// references to its DOM-elements.
var Visual = React.createClass({

  // We simply render an empty canvas and tell React to
  // store its reference after the render
  render: function() {
    return <canvas ref={this.handleRef} width={500} height={500}/>
  },

  handleRef: function(canvas) {
    this.canvas = canvas
  },

  // After the first render, we grab the reference
  // to the canvas element in the DOM and pass it
  // to the library
  componentDidMount: function() {
    // Here we also use some additional color configuration
    drawGraph(this.canvas, this.props.color)
  },

  // We also have some fine-granular control over the re-render
  // With the use of this lifecylce method
  shouldComponentUpdate: function(nextProps, nextState) {
    // Here we could tell our library the new data for props
    // or state, so it can update the DOM elements on its own

    // At the end we always return false so our render method
    // isn't called and the canvas element isn't replaced
    return false
  },

  // This lifecycle method can be used to free resources
  // before the component will be removed from the DOM.
  // Our canvas will be removed for sure, but often there
  // is state for the library, other objects, listeners etc.
  // they could be stored at this and should be deleted to
  // prevent memory leaks
  componentWillUnmount() {},

})

// Now we can use the library as a component
// No need for global IDs, every instance has its own canvas
// reference stored, also its own color property
var reactElement =
  <div>
    <Visual color='#f00'/>
    <Visual color='#0f0'/>
    <Visual color='#00f'/>
  </div>

ReactDOM.render(reactElement, document.getElementById('app'))

// Wrapping the library interaction into a function
function drawGraph(canvas, strokeColor) {

  // An example from http://bl.ocks.org/mbostock/1b64ec067fcfc51e7471d944f51f1611
  // its realeased under the GPL-V3

  var n = 20

  var nodes = d3.range(n * n).map(function(i) {
    return {index: i}
  })

  var links = []

  for (var y = 0; y < n; ++y) {
    for (var x = 0; x < n; ++x) {
      if (y > 0) links.push({source: (y - 1) * n + x, target: y * n + x});
      if (x > 0) links.push({source: y * n + (x - 1), target: y * n + x});
    }
  }

  d3.forceSimulation(nodes)
    .force('charge', d3.forceManyBody().strength(-30))
    .force('link', d3.forceLink(links).distance(20).iterations(10))
    .on('tick', ticked)

  var context = canvas.getContext('2d')
  var width = canvas.width
  var height = canvas.height

  function ticked() {
    context.clearRect(0, 0, width, height)
    context.save()
    context.translate(width / 2, height / 2)

    context.beginPath()
    links.forEach(drawLink)
    context.strokeStyle = '#aaa'
    context.stroke()

    context.beginPath()
    nodes.forEach(drawNode)
    context.fill()
    context.strokeStyle = strokeColor
    context.stroke()

    context.restore()
  }

  function drawLink(d) {
    context.moveTo(d.source.x, d.source.y)
    context.lineTo(d.target.x, d.target.y)
  }

  function drawNode(d) {
    context.moveTo(d.x + 3, d.y);
    context.arc(d.x, d.y, 3, 0, 2 * Math.PI)
  }

}

</script>